home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSIFile.cp < prev    next >
Encoding:
Text File  |  1994-01-06  |  26.0 KB  |  1,330 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIFile.cp        -    Implementation of file calls
  4. Author    :    Matthias Neeracher <neeri@iis.ethz.ch>
  5. Started    :    28May91                                Language    :    MPW C
  6.                 28May91    MN    Created
  7.                 28May91    MN    isatty()
  8.                 09Dec91    MN    Radical overhaul
  9.                 12Dec91     MN FSp2RelPath
  10.                 11Feb92    MN    Incorporated bug fix by John Reekie
  11.                 22Mar92    MN    Adapted for GUSI, change chdir stuff
  12.                 05Apr92    MN    lseek
  13.                 20Apr92    MN    C++ rewrite
  14.                 21May92    MN    Implemented select()
  15.                 15Jun92    MN    Separated path stuff
  16.                 21Jun92    MN    symlink()
  17.                 26Jun92    MN    symlink is starting to work
  18.                 27Jun92    MN    choose()
  19.                 13Jul92    MN    CopyIconFamily
  20.                 06Sep92    MN    Adapt alias resolution to new libraries
  21.                 08Sep92    MN    readlink()
  22.                 12Sep92    MN    Rename Paths.h to GUSIFSp_P.h
  23.                 15Sep92    MN    Slight error in do_stat()
  24.                 28Oct92    MN    Forgot to change to dirent
  25.                 15Nov92    MN    Rename GUSIFSp_P.h to TFileSpec.h (there we go again)
  26.                 28Nov92    MN    TEXT files are now considered executable
  27.                 08Dec92    MN    getcwd()
  28.                 13Dec92    MN    stat now returns DirID/FileNo, not parID for the ino field
  29.                 20Dec92    MN    do_putfile now respects default
  30.                 01Jan93    MN    fsetfileinfo
  31.                 03Jan93    MN    Respect configuration
  32.                 15Jan93    MN    choose() should *not* count the string terminator.
  33.                 15Jan93    MN    rename() has to be more careful about renaming order
  34.                 01Feb93    MN    Made nlink for directories return something meaningful
  35.                 23Feb93    MN    fgetfileinfo
  36.                 27Jun93    MN    f?truncate
  37.                 17Jul93    MN    Adapt to BSD 4.3, scandir
  38.                 25Aug93    MN    Need 2 include TextUtils since E.T.O. #12
  39.                 11Sep93    MN Trying to avoid stream.h unless absolutely necessary    
  40. Last        :    11Sep93
  41. *********************************************************************/
  42.  
  43. #include "GUSI_P.h"
  44. #include "TFileSpec.h"
  45.  
  46. #include <Errors.h>
  47. #include <Resources.h>
  48. #include <Script.h>
  49. #include <Finder.h>
  50. #include <Folders.h>
  51. #include <Devices.h>
  52. #include <Memory.h>
  53. #include <Aliases.h>
  54. #include <string.h>
  55. #include <PLStringFuncs.h>
  56. #include <ioctl.h>
  57. #include <fcntl.h>
  58. #include <errno.h>
  59. #ifdef GUSI_FILE_DEBUG
  60. #include <stream.h>
  61. #endif
  62. #include <StdLib.h>
  63. #include <Time.h>
  64. #include <TextUtils.h>
  65. #include "unistd.h"
  66.  
  67. FileSocketDomain    FileSockets;
  68.  
  69. class FileSocket : public Socket {
  70.     friend class FileSocketDomain;
  71. protected:
  72.     int    fd;
  73.                     FileSocket(int fd)    :    fd(fd)    {}
  74. public:
  75.     virtual int    read(void * buffer, int buflen);
  76.     virtual int write(void * buffer, int buflen);
  77.     virtual int    fcntl(unsigned int cmd, int arg);
  78.     virtual int    ioctl(unsigned int request, void *argp);
  79.     virtual int    fstat(struct stat * buf);
  80.     virtual long lseek(long offset, int whence);
  81.     virtual int ftruncate(long offset);
  82.     virtual int    isatty();
  83.     virtual int select(Boolean * canRead, Boolean * canWrite, Boolean * exception);
  84.     virtual         ~FileSocket();
  85. };
  86.  
  87. /*********************** Prototypes for stdio ***********************/
  88.  
  89. extern "C" {
  90. int file_open(const char * name, int flags);
  91. int file_close(int s);
  92. int file_read(int s, char *buffer, unsigned buflen);
  93. int file_write(int s, char *buffer, unsigned buflen);
  94. int file_fcntl(int s, unsigned int cmd, int arg);
  95. int file_dup(int s);
  96. int file_ioctl(int d, unsigned int request, long *argp);  /* argp is really a caddr_t */
  97. long file_lseek(int fd, long offset, int whence);
  98. int file_faccess(char *fileName, unsigned int cmd, long * arg);
  99. }
  100.  
  101. /********************* FileSocketDomain members *********************/
  102.  
  103. Socket * FileSocketDomain::stdopen(int fd)
  104. {
  105.     return new FileSocket(fd);
  106. }
  107.  
  108. Boolean IsDevice(const char * fn)
  109. {
  110.     return     (    
  111.         (fn[0] | 0x20) == 'd'
  112.     && (fn[1] | 0x20) == 'e'
  113.     && (fn[2] | 0x20) == 'v'
  114.     && fn[3] == ':');
  115. }
  116.  
  117. static int File_error(OSErr err)
  118. {
  119.     switch (err) {
  120.     case noErr:
  121.         errno = 0;
  122.         
  123.         return 0;
  124.     case bdNamErr:
  125.         return GUSI_error(ENOTDIR);
  126.     case fnfErr:
  127.     case dirNFErr:
  128.         return GUSI_error(ENOENT);
  129.     case dupFNErr:
  130.         return GUSI_error(EEXIST);
  131.     case dirFulErr:
  132.         return GUSI_error(ENOSPC);
  133.     case fBsyErr:
  134.         return GUSI_error(EIO);
  135.     default:
  136.         return GUSI_error(EINVAL);
  137.     }
  138. }
  139.  
  140. Socket * FileSocketDomain::open(const char * filename, int oflag)
  141. {
  142.     int            fd;
  143.     Boolean        fresh;
  144.     char *        name;
  145.     
  146.     if (IsDevice(filename))    {
  147.         DeviceSocketDomain *     dom;
  148.         Socket *                     sock;
  149.         
  150.         // Try to hand it off to a specialized device handler
  151.  
  152.         for (dom = DeviceSocketDomain::firstDev; dom; dom = dom->nextDev) 
  153.             if ((sock = dom->open(filename+4, oflag)) != DeviceSocketDomain::TryNextDevice)
  154.                 return sock;
  155.         
  156.         fresh    =    false;
  157.         fd     =     file_open(filename, oflag);
  158.     } else {
  159.         TFileSpec spec(filename, oflag & O_ALIAS);
  160.         
  161.         if (spec.Error())    {
  162.             File_error(spec.Error());
  163.             
  164.             return nil;
  165.         }
  166.         
  167.         fresh    = (oflag & O_CREAT) && !spec.Exists();
  168.         fd        = file_open(name = spec.RelPath(), oflag);
  169.         
  170.         if ((fd != -1) && fresh)
  171.             GUSIConfig.SetDefaultFType(spec);
  172.     }
  173.     
  174.     if (fd == -1)
  175.         return nil;
  176.     
  177.     Socket * sock    =    new FileSocket(fd);
  178.     
  179.     if (!sock)    {
  180.         file_close(fd);
  181.         
  182.         if (fresh)
  183.             remove(name);
  184.     } 
  185.     
  186.     return sock;
  187. }
  188.  
  189. #define SFSaveDisk        (* (short *) 0x0214)
  190. #define CurDirStore        (* (long *)  0x0398)
  191.  
  192. static long     currentDir;
  193. static SFReply    reply;
  194. static char *    customPrompt;
  195.  
  196. static int do_getfile(
  197.                     TFileSpec *     defaultFile, 
  198.                     TFileSpec *     result,
  199.                     short                numTypes,
  200.                     SFTypeList        types)
  201. {
  202.     Point        myPoint    =    {75, 75};
  203.     
  204.     if (defaultFile) {
  205.         *defaultFile += "\p";
  206.         
  207.         SFSaveDisk     = -defaultFile->vRefNum;
  208.         CurDirStore = defaultFile->parID;
  209.     }
  210.     
  211.     SFGetFile(myPoint, "\p", NULL, numTypes, types, NULL, &reply);
  212.     
  213.     if (reply.good)
  214.         *result = TFileSpec(reply.vRefNum, reply.fName);
  215.     
  216.     return reply.good;
  217. }
  218.  
  219. static pascal Boolean FolderFFilter(ParmBlkPtr p)
  220. {
  221.     return !(p->fileParam.ioFlAttrib & ioDirMask);
  222. }
  223.  
  224. static ControlHandle GetDlgCtrl(DialogPtr dlg, short item)
  225. {
  226.     short     kind;
  227.     Handle    hdl;
  228.     Rect        box;
  229.     
  230.     GetDItem(dlg, item, &kind, &hdl, &box);
  231.     return (ControlHandle) hdl;
  232. }
  233.  
  234. static pascal short GetDirDlgHook(short item, DialogPtr dlgPtr)
  235. {
  236.     switch (item) {
  237.     case sfHookFirstCall:
  238.         if (customPrompt)
  239.             setitext(Handle(GetDlgCtrl(dlgPtr, 13)), customPrompt);
  240.         break;
  241.     case 11:
  242.         if (reply.fType) {
  243.             if (!reply.fName[0])
  244.                 currentDir    =    reply.fType;
  245.             else {
  246.                 TFileSpec dir(reply.vRefNum, CurDirStore, reply.fName);
  247.     
  248.                 dir += "\p";
  249.                 
  250.                 currentDir = dir.parID;
  251.             }
  252.                 
  253.             return    1;
  254.         }
  255.         break;
  256.     
  257.     case 12:
  258.         currentDir    =    CurDirStore;
  259.         
  260.         return 1;
  261.     case sfHookNullEvent:
  262.         if (!reply.fType)
  263.             HiliteControl(GetDlgCtrl(dlgPtr, 11), 255);
  264.         else
  265.             HiliteControl(GetDlgCtrl(dlgPtr, 11), 0);
  266.         break;
  267.     }
  268.     
  269.     return item;
  270. }
  271.  
  272. static int do_getfolder(
  273.                     char *            prompt,
  274.                     TFileSpec *     defaultFile, 
  275.                     TFileSpec *     result)
  276. {
  277.     Point        myPoint    =    {75, 75};
  278.     
  279.     if (defaultFile) {
  280.         *defaultFile += "\p";
  281.         
  282.         SFSaveDisk     = -defaultFile->vRefNum;
  283.         CurDirStore = defaultFile->parID;
  284.     }
  285.     
  286.     customPrompt = prompt && *prompt ? prompt : nil;
  287.     
  288.     SFPGetFile(
  289.         myPoint, 
  290.         "\p", 
  291.         FolderFFilter, 
  292.         -1, 
  293.         nil, 
  294.         GetDirDlgHook, 
  295.         &reply,
  296.         GUSIRsrcID,                
  297.         nil);
  298.  
  299.     if (reply.good) {
  300.         result->vRefNum    =    -SFSaveDisk;
  301.         result->parID        =    currentDir;
  302.         --*result;
  303.     }
  304.     
  305.     return reply.good;
  306. }
  307.  
  308. static int     do_putfile(    
  309.                     char    *            prompt,
  310.                     TFileSpec *     defaultFile, 
  311.                     TFileSpec *     result)
  312. {
  313.     Point            myPoint    =    {75, 75};
  314.     StringPtr    defName;
  315.     Str255        prmpt;
  316.     
  317.     prompt = (char *) memccpy((char *) prmpt + 1, prompt, 0, 254);
  318.     
  319.     if (prompt)
  320.         prmpt[0] = prompt - (char *) prmpt - 2;
  321.     else
  322.         prmpt[0] = 254;
  323.         
  324.     if (defaultFile) {
  325.         SFSaveDisk     = -defaultFile->vRefNum;
  326.         CurDirStore = defaultFile->parID;
  327.         defName        = defaultFile->name;
  328.     } else
  329.         defName        = "\p";
  330.     
  331.     SFPutFile(myPoint, prmpt, defName, NULL, &reply);
  332.     
  333.     if (reply.good)
  334.         *result        = TFileSpec(reply.vRefNum, reply.fName);
  335.     
  336.     return reply.good;
  337. }
  338.  
  339. int FileSocketDomain::choose(int, char * prompt, void * constraint, int flags, void * name, int * namelen)
  340. {
  341.     sa_constr_file *     constr = (sa_constr_file *) constraint;
  342.     TFileSpec            file;
  343.     TFileSpec            df;
  344.     TFileSpec *            defaultFile;
  345.     Boolean                good;
  346.     char *                path;
  347.     int                    len;
  348.     
  349.     if (flags & CHOOSE_DEFAULT) {
  350.         df = TFileSpec((char *) name);
  351.         defaultFile = df.Error() ? nil : &df;
  352.     } else
  353.         defaultFile = nil;
  354.         
  355.     if (flags & CHOOSE_NEW)
  356.         good = do_putfile(prompt, defaultFile, &file);
  357.     else if (flags & CHOOSE_DIR)
  358.         good = do_getfolder(prompt, defaultFile, &file);
  359.     else if (constr)
  360.         good = do_getfile(defaultFile, &file, constr->numTypes, constr->types);
  361.     else
  362.         good = do_getfile(defaultFile, &file, -1, nil);
  363.     
  364.     if (good) {
  365.         path    =    file.FullPath();
  366.         len    =    strlen(path);
  367.         
  368.         if (len < *namelen)
  369.             memcpy(name, path, (*namelen = len)+1);
  370.         else
  371.             return GUSI_error(EINVAL);
  372.     } else
  373.         return GUSI_error(EINTR);
  374.     
  375.     return 0;
  376. }
  377.  
  378. /************************ FileSocket members ************************/
  379.  
  380. int FileSocket::read(void * buffer, int buflen)
  381. {
  382.     return file_read(fd, (char *) buffer, buflen);
  383. }
  384.  
  385. int FileSocket::write(void * buffer, int buflen)
  386. {
  387.     return file_write(fd, (char *) buffer, buflen);
  388. }
  389.  
  390. int FileSocket::fcntl(unsigned int cmd, int arg)
  391. {
  392.     return file_fcntl(fd, cmd, arg);
  393. }
  394.  
  395. int FileSocket::ioctl(unsigned int request, void *argp)
  396. {
  397.     return file_ioctl(fd, request, (long *) argp);
  398. }
  399.  
  400. static OSErr GetFDCatInfo(int fd, CInfoPBRec & cb);
  401. static OSErr GetVolume(CInfoPBRec & cb, ParamBlockRec & pb);
  402. static int do_stat(const CInfoPBRec & cb, const ParamBlockRec & pb, struct stat & buf);
  403.  
  404. int FileSocket::fstat(struct stat * buf)
  405. {
  406.     CInfoPBRec        cb;
  407.     ParamBlockRec    pb;
  408.  
  409.     if (GetFDCatInfo(fd, cb)) {
  410.     
  411.         // Pseudofile
  412.         
  413.         buf->st_dev            =    0;
  414.         buf->st_ino            =    0;
  415.         buf->st_mode        =    S_IFCHR | 0666;
  416.         buf->st_nlink        =    1;
  417.         buf->st_uid            =    0;
  418.         buf->st_gid            =    0;
  419.         buf->st_rdev        =    0;
  420.         buf->st_size        =    1;
  421.         buf->st_atime        =    time(NULL);
  422.         buf->st_mtime        =    time(NULL);
  423.         buf->st_ctime        =    time(NULL);
  424.         buf->st_blksize    =    1;
  425.         buf->st_blocks        =    1;
  426.  
  427.         return 0;
  428.     } else if (GetVolume(cb, pb)) {
  429.         errno = ENOENT;
  430.  
  431.         return -1;
  432.     } else
  433.         return do_stat(cb, pb, *buf);
  434. }
  435.  
  436. long FileSocket::lseek(long offset, int whence)
  437. {
  438.     long    res;
  439.     Ptr    buf;
  440.  
  441.     res = file_lseek(fd, offset, whence);
  442.     
  443.     if (res != -1)
  444.         return res;
  445.         
  446.     if (whence != SEEK_SET) {
  447.         res = file_lseek(fd, 0, whence);
  448.         
  449.         if (res == -1)
  450.             return res;
  451.             
  452.         offset += res;
  453.     }
  454.     
  455.     res = file_lseek(fd, 0, SEEK_END);
  456.     
  457.     buf = NewPtrClear(1024);
  458.     
  459.     while (offset >= res + 1024)
  460.         if (file_write(fd, buf, 1024) == -1)
  461.             return -1;
  462.         else
  463.             res += 1024;
  464.  
  465.     if (offset > res && file_write(fd, buf, unsigned(offset-res)) == -1)
  466.         return -1;
  467.  
  468.     return 0;
  469. }
  470.  
  471. int FileSocket::ftruncate(long offset)
  472. {
  473.     if (lseek(offset, SEEK_SET) == -1)
  474.         return -1;
  475.     
  476.     if (file_ioctl(fd, FIOSETEOF, (long *) offset) == -1)
  477.         return -1;
  478.     
  479.     return 0;
  480. }
  481.  
  482. int FileSocket::isatty()
  483. {
  484.     short    fRef;
  485.     
  486.     return file_ioctl(fd, FIOREFNUM, (long *) &fRef) == -1;
  487. }
  488.  
  489. int FileSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  490. {
  491.     int    goodies    =    0;
  492.     
  493.     // Simplicistic implementation 
  494.     
  495.     if (canRead)    {
  496.         *canRead = true;
  497.         ++goodies;
  498.     }
  499.     
  500.     if (canWrite)    {
  501.         *canWrite = true;
  502.         ++goodies;
  503.     }
  504.     
  505.     return goodies;
  506. }
  507.  
  508. FileSocket::~FileSocket()
  509. {
  510.     file_close(fd);
  511. }
  512.  
  513. /***************** Things that happen to files only *****************/
  514.  
  515. int creat(const char* filename)
  516. {
  517.     return open(filename, O_WRONLY | O_TRUNC | O_CREAT);
  518. }
  519.  
  520. int remove(const char *filename)
  521. {
  522.     return faccess((char*) filename, F_DELETE, 0);
  523. }
  524.  
  525. int unlink(char*filename)
  526. {
  527.     return faccess((char*) filename, F_DELETE, 0);
  528. }
  529.  
  530. int rename(const char *oldname, const char *newname)
  531. {    
  532.     if (IsDevice(oldname))
  533.         return GUSI_error(EINVAL);
  534.     if (IsDevice(newname))
  535.         return GUSI_error(EINVAL);
  536.         
  537.     TFileSpec    oldnm(oldname, true);
  538.     TFileSpec    newnm(newname, true);
  539.     
  540.     return File_error(FSpSmartMove(&oldnm, &newnm));
  541. }
  542.  
  543. void fsetfileinfo(char *filename, unsigned long newcreator, unsigned long newtype)
  544. {
  545.     if (IsDevice(filename)) {
  546.         GUSI_error(EINVAL);
  547.         
  548.         return;
  549.     }
  550.         
  551.     TFileSpec    name(filename);
  552.     FInfo            info;    
  553.  
  554.     if (name.Error() || !name.Exists() || HGetFInfo(name.vRefNum, name.parID, name.name, &info)) {
  555.         GUSI_error(EIO);
  556.         
  557.         return;
  558.     }
  559.     
  560.     info.fdType     =    newtype;
  561.     info.fdCreator    =    newcreator;
  562.     
  563.     if (HSetFInfo(name.vRefNum, name.parID, name.name, &info))
  564.         GUSI_error(EIO);
  565.     
  566.     errno = 0;
  567. }
  568.  
  569. void fgetfileinfo(char *filename, unsigned long * creator, unsigned long * type)
  570. {
  571.     if (IsDevice(filename)) {
  572.         GUSI_error(EINVAL);
  573.         
  574.         return;
  575.     }
  576.         
  577.     TFileSpec    name(filename);
  578.     FInfo            info;    
  579.  
  580.     if (name.Error() || !name.Exists() || HGetFInfo(name.vRefNum, name.parID, name.name, &info)) {
  581.         GUSI_error(EIO);
  582.         
  583.         return;
  584.     }
  585.     
  586.     if (creator)
  587.         *creator = info.fdCreator;
  588.     
  589.     if (type)
  590.         *type = info.fdType;
  591.     
  592.     errno = 0;
  593. }
  594.  
  595. OSErr VRef2Icon(short vRef, Handle * icon)
  596. {
  597.     OSErr                err;
  598.     HParmBlkPtr        hpp;
  599.     ParmBlkPtr        pp;
  600.     HParamBlockRec    hpb;
  601.     
  602.     hpp                                =    &hpb;
  603.     pp                                    =    (ParmBlkPtr) hpp;
  604.     hpp->volumeParam.ioVRefNum    =    vRef;
  605.     hpp->volumeParam.ioNamePtr    =    nil;
  606.     hpp->volumeParam.ioVolIndex=    0;
  607.     if (err = PBHGetVInfoSync(hpp))
  608.         return err;
  609.         
  610.     pp->cntrlParam.ioVRefNum    =    hpb.volumeParam.ioVDrvInfo;
  611.     pp->cntrlParam.ioCRefNum    =    hpb.volumeParam.ioVDRefNum;
  612.     pp->cntrlParam.csCode        =    21;
  613.     
  614.     if (err = PBControlSync(pp))
  615.         return err;
  616.     
  617.     PtrToHand(*(Ptr *) pp->cntrlParam.csParam, icon, 256);
  618.     
  619.     return noErr;
  620. }
  621.  
  622. typedef OSType    TTypeMap[2];
  623.  
  624. static TTypeMap map[]    =    {
  625.     {'amnu', 'faam'},
  626.     {'ctrl', 'fact'},
  627.     {'extn', 'faex'},
  628.     {'pref', 'fapf'},
  629.     {'prnt', 'fapn'},
  630.     {'empt', 'trsh'},
  631.     {'trsh', 'trsh'},
  632.     {'strt', 'fast'},
  633.     {'macs', 'fasy'},
  634.     {     0,      0}
  635. };
  636.  
  637. #ifdef GUSI_FILE_DEBUG
  638. ostream & operator<<(ostream & str, OSType * ty)
  639. {
  640.     return str     << "'"
  641.                     << char((*ty >> 24) & 0xFF) 
  642.                     << char((*ty >> 16) & 0xFF)
  643.                     << char((*ty >> 8) & 0xFF)
  644.                     << char(*ty & 0xFF)
  645.                     << "'";
  646. }
  647. #endif
  648.  
  649. void OurResidentAliasExpert(
  650.     TFileSpec & file, 
  651.     OSType *     fCreator, 
  652.     OSType *     fType,
  653.     TFileSpec * iconFile,
  654.     short *         iconID)
  655. {
  656.     Boolean                        appleShare;
  657.     CInfoPBRec                    info;
  658.     GetVolParmsInfoBuffer    volParms;
  659.     HParamBlockRec                pb;
  660.     
  661.     *fCreator = 'MACS';
  662.     *iconFile    =    file;
  663.     *iconID        =    kCustomIconResource;
  664.     
  665.     if (file.parID == fsRtParID)
  666.         appleShare    =    true;
  667.     else {
  668.         if (file.CatInfo(info))
  669.             goto error;
  670.         
  671.         appleShare = !IsFile(info);
  672.     }
  673.     
  674.     if (appleShare)    {
  675.         pb.ioParam.ioNamePtr    =    nil;
  676.         pb.ioParam.ioVRefNum    =    file.vRefNum;
  677.         pb.ioParam.ioBuffer    =    Ptr(&volParms);
  678.         pb.ioParam.ioReqCount=    sizeof(GetVolParmsInfoBuffer);
  679.         
  680.         if (PBHGetVolParmsSync(&pb) || !volParms.vMServerAdr)    
  681.             appleShare    =    false;
  682.     }
  683.     
  684.     if (appleShare)
  685.         if (file.parID == fsRtParID)
  686.             *fType    =    'srvr';
  687.         else if (!HasRdPerm(info))
  688.             *fType     =    'fadr';
  689.         else
  690.             *fType    =    'faet';
  691.     else if (file.parID == fsRtParID)
  692.         *fType    =    'hdsk';
  693.     else if (!IsFile(info)) 
  694.         if (DirIsMounted(info))
  695.             *fType = 'famn';
  696.         else if (DirIsExported(info))
  697.             *fType = 'fash';
  698.         else if (DirIsShared(info))
  699.             *fType = 'faet';
  700.         else 
  701.             *fType = 'fdrp';
  702.  
  703.     if (file.parID == fsRtParID)    {
  704.         iconFile->parID    =    fsRtDirID;
  705.         PLstrcpy(iconFile->name, "\pIcon\n");
  706.     } else if (!IsFile(info))    {
  707.         if (info.dirInfo.ioDrDirID < 9)    {
  708.             short vRef;
  709.             long    dirID;
  710.             
  711.             for (TTypeMap * mapp = map; **mapp; ++mapp)
  712.                 if (!FindFolder(file.vRefNum, (*mapp)[0], false, &vRef, &dirID))
  713.                     if (dirID == info.dirInfo.ioDrDirID) {
  714.                         *fType = (*mapp)[1];
  715.  
  716.                         break;
  717.                     }
  718.         }
  719.         *iconFile += "\pIcon\n";
  720.     } else {
  721.         *fType    =    info.hFileInfo.ioFlFndrInfo.fdType;
  722.         *fCreator=    info.hFileInfo.ioFlFndrInfo.fdCreator;
  723.     }
  724.     
  725. #ifdef GUSI_FILE_DEBUG
  726.     cerr << "Type = " << fType << ", creator = " << fCreator << endl;
  727.     cerr << "Look for custom icons in " << iconFile->FullPath() << endl;
  728. #endif
  729.  
  730.     return;
  731. error:
  732.     *fType        =    0;
  733.     *fCreator    =    0;
  734. }
  735.  
  736. static OSType iconTypes[]    =    {
  737.     'ICN#',
  738.     'ics#',
  739.     'icl4',
  740.     'ics4',
  741.     'icl8', 
  742.     'ics8',
  743.     0
  744. };
  745.  
  746. Boolean CopyIconFamily(short srcResFile, short srcID, short dstResFile, short dstID)
  747. {
  748.     Handle    icon;
  749.     Boolean    success    =    false;
  750.     OSType * types;
  751.  
  752.     for (types = iconTypes; *types; ++types)    {
  753.         UseResFile(srcResFile);
  754.         if (icon = Get1Resource(*types, srcID))    {
  755.             UseResFile(dstResFile);
  756.             DetachResource(icon);
  757.             AddResource(icon, *types, dstID, "\p");
  758.         
  759.             success = success || !ResError();
  760.         }
  761.     }
  762.     
  763.     return success;
  764. }
  765.  
  766. Boolean AddIconsToFile(
  767.     const TFileSpec &    origFile,
  768.     short                 aliasFile, 
  769.     OSType                 fCreator, 
  770.     OSType                fType, 
  771.     const FSSpec &        iconFile, 
  772.     short                    iconID)
  773. {
  774.     short        iFile;
  775.     Boolean    success;
  776.     Handle     icon;
  777.  
  778.     iFile = FSpOpenResFile(&iconFile, fsRdPerm);
  779.     
  780.     if (iFile == -1)
  781.         goto noCustom;
  782.     
  783.     success = CopyIconFamily(iFile, iconID, aliasFile, kCustomIconResource);
  784.     
  785.     CloseResFile(iFile);
  786.     
  787.     if (success)
  788.         return true;
  789.  
  790. #ifdef GUSI_FILE_DEBUG
  791.     cerr << "No custom Icons found." << endl;
  792. #endif
  793.  
  794. noCustom:    
  795.     if (fType == 'hdsk' && fCreator == 'MACS')
  796.         if (!VRef2Icon(origFile.vRefNum, &icon))    {
  797. #ifdef GUSI_FILE_DEBUG
  798.             cerr << "Found icon for disk drive." << endl;
  799. #endif
  800.             AddResource(icon, 'ICN#', kCustomIconResource, "\p");
  801.             
  802.             return !ResError();
  803.         }
  804.                 
  805.     return false;
  806. }
  807.  
  808. int symlink(const char* linkto, const char* linkname)
  809. {
  810.     if (IsDevice(linkto))
  811.         return GUSI_error(EINVAL);
  812.     if (IsDevice(linkname))
  813.         return GUSI_error(EINVAL);
  814.         
  815.     OSType        fType;
  816.     OSType        fCreator;
  817.     short            iconID;
  818.     short            aliasFile;
  819.     AliasHandle    alias;
  820.     Boolean        customIcon;
  821.     TFileSpec    iconFile;
  822.     FInfo            info;
  823.     
  824.     if (!hasAlias || !hasMakeFSSpec)
  825.         return GUSI_error(EOPNOTSUPP);
  826.         
  827.     TFileSpec    oldnm(linkto);
  828.     
  829.     if (oldnm.Error() || !oldnm.Exists())
  830.         return GUSI_error(EIO);
  831.         
  832.     TFileSpec    newnm(linkname, true);
  833.  
  834.     if (newnm.Error())
  835.         return GUSI_error(EIO);
  836.     
  837.     if (newnm.Exists())
  838.         return GUSI_error(EEXIST);
  839.     
  840.     OurResidentAliasExpert(oldnm, &fCreator, &fType, &iconFile, &iconID);
  841.     
  842. #ifdef GUSI_FILE_DEBUG
  843.     cerr << "Creating " << newnm.FullPath() << endl;
  844. #endif
  845.  
  846.     FSpCreateResFile(&newnm, fCreator, fType, smSystemScript);
  847.     
  848.     if (ResError())
  849.         return GUSI_error(EIO);
  850.     
  851. #ifdef GUSI_FILE_DEBUG
  852.     cerr << "Opening " << newnm.FullPath() << endl;
  853. #endif
  854.  
  855.     aliasFile = FSpOpenResFile(&newnm, fsRdWrPerm);
  856.     
  857.     if (aliasFile == -1)
  858.         goto deleteFile;
  859.     
  860. #ifdef GUSI_FILE_DEBUG
  861.     cerr << "Creating alias for " << oldnm.FullPath() << " in " << newnm.FullPath() << endl;
  862. #endif
  863.     
  864.     if (NewAlias(nil, &oldnm, &alias))
  865.         goto closeFile;
  866.  
  867. #ifdef GUSI_FILE_DEBUG
  868.     cerr << "Adding alias to file." << endl;
  869. #endif
  870.     
  871.     AddResource((Handle) alias, 'alis', 0, oldnm.name);
  872.     
  873.     if (ResError())
  874.         goto deleteAlias;
  875.     
  876.     customIcon = AddIconsToFile(oldnm, aliasFile, fCreator, fType, iconFile, iconID);
  877.  
  878. #ifdef GUSI_FILE_DEBUG
  879.     cerr << "There were " << (customIcon ? "" : "no ") << "custom Icons." << endl;
  880. #endif
  881.         
  882.     CloseResFile(aliasFile);
  883.  
  884.     FSpGetFInfo(&newnm, &info);
  885.     info.fdFlags    |=    (1 << 15) | (customIcon ? (1 << 10) : 0);
  886.     info.fdFlags    &= ~(1 << 8);
  887.     FSpSetFInfo(&newnm, &info);
  888.     
  889.     return 0;
  890.  
  891. deleteAlias:
  892.     DisposHandle((Handle) alias);
  893. closeFile:
  894.     CloseResFile(aliasFile);
  895. deleteFile:
  896.     FSpDelete(&newnm);    
  897.     
  898.     return GUSI_error(EIO);
  899. }
  900.  
  901. int readlink(const char * path, char * buf, int bufsiz)
  902. {
  903.     if (IsDevice(path))
  904.         return GUSI_error(EINVAL);
  905.         
  906.     char *         end;
  907.     TFileSpec    file(path, true);
  908.     CInfoPBRec    info;
  909.     
  910.     if (file.CatInfo(info))
  911.         goto error;
  912.         
  913.     if (!IsAlias(info))
  914.         return GUSI_error(EINVAL);
  915.     
  916.     if (file.Resolve())
  917.         goto error;
  918.  
  919.     end = (char *) memccpy(buf, file.FullPath(), 0, bufsiz);
  920.     
  921.     if (!end)
  922.         return GUSI_error(ENAMETOOLONG);
  923.     else
  924.         return end - buf - 1;
  925.         
  926. error:
  927.     return File_error(file.Error());
  928. }
  929.  
  930. int faccess(char* filename, unsigned int cmd, long* arg)
  931. {
  932.     if (IsDevice(filename))
  933.         return file_faccess(filename, cmd, arg);
  934.         
  935.     TFileSpec    file(filename, cmd == F_DELETE);
  936.     
  937.     if (file.Error())
  938.         return File_error(file.Error());
  939.     else
  940.         return file_faccess(file.RelPath(), cmd, arg);
  941. }
  942.  
  943. int truncate(const char* filename, off_t offset)
  944. {
  945.     int    res;
  946.     int    fd    =     open(filename, O_RDWR);
  947.     
  948.     if (fd == -1)
  949.         return -1;
  950.     
  951.     res = ftruncate(fd, offset);
  952.  
  953.     close(fd);
  954.     
  955.     return res;
  956. }
  957.  
  958. static OSErr GetFDCatInfo(int fd, CInfoPBRec & cb)
  959. {
  960.     short                fRef;
  961.     OSErr                err;
  962.     FCBPBRec            fcb;
  963.     Str255            fname;
  964.  
  965.     if (file_ioctl(fd, FIOREFNUM, (long *) &fRef) == -1)
  966.         return fnfErr;
  967.  
  968.     fcb.ioNamePtr    =     fname;
  969.     fcb.ioRefNum    =    fRef;
  970.     fcb.ioFCBIndx    =     0;
  971.     if (err = PBGetFCBInfoSync(&fcb))
  972.         return err;
  973.  
  974.     cb.hFileInfo.ioNamePtr        =    fname;
  975.     cb.hFileInfo.ioDirID            =    fcb.ioFCBParID;
  976.     cb.hFileInfo.ioVRefNum        =    fcb.ioFCBVRefNum;
  977.     cb.hFileInfo.ioFDirIndex    =    0;
  978.  
  979.     return PBGetCatInfoSync(&cb);
  980. }
  981.  
  982. static OSErr GetVolume(CInfoPBRec & cb, ParamBlockRec & pb)
  983. {
  984.     Str63                name;
  985.  
  986.     pb.volumeParam.ioNamePtr    =    name;
  987.     pb.volumeParam.ioVRefNum    =    cb.hFileInfo.ioVRefNum;
  988.     pb.volumeParam.ioVolIndex    =    0;
  989.  
  990.     return PBGetVInfo(&pb, false);
  991. }
  992.  
  993. static int do_stat(const CInfoPBRec & cb, const ParamBlockRec & pb, struct stat & buf)
  994. {
  995.     buf.st_dev        =    pb.ioParam.ioVRefNum;
  996.     buf.st_ino        =    cb.dirInfo.ioDrDirID;
  997.     buf.st_nlink    =    1;
  998.     buf.st_uid        =    0;
  999.     buf.st_gid        =    0;
  1000.     buf.st_rdev        =    0;
  1001.     buf.st_atime    =    cb.hFileInfo.ioFlMdDat;
  1002.     buf.st_mtime    =    cb.hFileInfo.ioFlMdDat;
  1003.     buf.st_ctime    =    cb.hFileInfo.ioFlCrDat;
  1004.     buf.st_blksize    =    pb.volumeParam.ioVAlBlkSiz;
  1005.  
  1006.     if (!IsFile(cb))    {
  1007.         TFileSpec        spec;
  1008.         CInfoPBRec        info;
  1009.  
  1010.         spec.vRefNum    =    pb.ioParam.ioVRefNum;
  1011.         spec.parID        =    cb.dirInfo.ioDrDirID;
  1012.         
  1013.         ++buf.st_nlink;
  1014.         
  1015.         if (GUSIConfig.accurStat) {
  1016.             for (int i = 0; i++ < cb.dirInfo.ioDrNmFls;) {
  1017.                 spec    =    spec[i];
  1018.                 if (!spec.Error() && !spec.CatInfo(info) && !IsFile(info))
  1019.                     ++buf.st_nlink;
  1020.             }
  1021.         } else {
  1022.             buf.st_nlink    +=    cb.dirInfo.ioDrNmFls;
  1023.         }
  1024.         
  1025.         buf.st_mode    =    S_IFDIR | 0777;
  1026.         buf.st_size    =    cb.dirInfo.ioDrNmFls;
  1027.     } else if (IsAlias(cb)) {
  1028.         buf.st_mode    =    S_IFLNK | 0777;
  1029.         buf.st_size    =    cb.hFileInfo.ioFlRLgLen;        /* Data fork is ignored    */
  1030.     } else if (cb.hFileInfo.ioFlFndrInfo.fdType == '∑OCK') {
  1031.         buf.st_mode    =    S_IFSOCK | 0666;
  1032.         buf.st_size    =    cb.hFileInfo.ioFlRLgLen;        /* Data fork is ignored    */
  1033.     } else {
  1034.         buf.st_mode    =    S_IFREG | 0666;
  1035.  
  1036.         if (cb.hFileInfo.ioFlAttrib & 0x01)
  1037.             buf.st_mode&=    ~0444;
  1038.  
  1039.         switch (cb.hFileInfo.ioFlFndrInfo.fdType) {
  1040.         case 'APPL':
  1041.         case 'MPST':
  1042.         case 'TEXT':
  1043.             buf.st_mode|=    0111;
  1044.             break;
  1045.         default:
  1046.             break;
  1047.         }
  1048.         
  1049.         buf.st_size    =    cb.hFileInfo.ioFlLgLen;        /* Resource fork is ignored    */
  1050.     }
  1051.  
  1052.     buf.st_blocks    =    (buf.st_size + buf.st_blksize - 1) / buf.st_blksize;
  1053.  
  1054.     return 0;
  1055. }
  1056.  
  1057. static int do_special_stat(struct stat & buf)
  1058. {
  1059.     buf.st_dev            =    0;
  1060.     buf.st_ino            =    0;
  1061.     buf.st_mode            =    S_IFCHR | 0666 ;
  1062.     buf.st_nlink        =    1;
  1063.     buf.st_uid            =    0;
  1064.     buf.st_gid            =    0;
  1065.     buf.st_rdev            =    0;
  1066.     buf.st_size            =    1;
  1067.     buf.st_atime        =    time(NULL);
  1068.     buf.st_mtime        =    time(NULL);
  1069.     buf.st_ctime        =    time(NULL);
  1070.     buf.st_blksize        =    1;
  1071.     buf.st_blocks        =    1;
  1072.     
  1073.     return 0;
  1074. }
  1075.  
  1076. int stat(const char * path, struct stat * buf)
  1077. {
  1078.     CInfoPBRec        cb;
  1079.     ParamBlockRec    pb;
  1080.     
  1081.     if (IsDevice(path)) {
  1082.         return do_special_stat(*buf);
  1083.     } else {
  1084.         TFileSpec        spec(path);
  1085.     
  1086.         if (spec.Error() || spec.CatInfo(cb) || GetVolume(cb, pb)) {
  1087.             errno = ENOENT;
  1088.     
  1089.             return -1;
  1090.         } else 
  1091.             return do_stat(cb, pb, *buf);
  1092.     }
  1093. }
  1094.  
  1095. int    lstat(const char * path, struct stat * buf)
  1096. {
  1097.     CInfoPBRec        cb;
  1098.     ParamBlockRec    pb;
  1099.  
  1100.     if (IsDevice(path)) {
  1101.         return do_special_stat(*buf);
  1102.     } else {
  1103.         TFileSpec        spec(path, true);
  1104.     
  1105.         if (spec.Error() || spec.CatInfo(cb) || GetVolume(cb, pb)) {
  1106.             errno = ENOENT;
  1107.     
  1108.             return -1;
  1109.         } else 
  1110.             return do_stat(cb, pb, *buf);
  1111.     }
  1112. }
  1113.  
  1114. /************************* directory stuff **************************/
  1115.  
  1116. struct dir {
  1117.     short            index;
  1118.     short            vol;
  1119.     long            dirID;
  1120.     dirent        entry;
  1121. };
  1122.  
  1123. DIR * opendir(const char * name)
  1124. {
  1125.     DIR *            d;
  1126.     TFileSpec    spec(name);
  1127.     
  1128.     if (spec.Error())
  1129.         goto error;
  1130.     
  1131.     spec += "\p";
  1132.     
  1133.     if (spec.Error())
  1134.         goto error;
  1135.     
  1136.     d = (DIR *) NewPtr(sizeof(DIR));
  1137.     
  1138.     d->index    =    1;
  1139.     d->vol    =    spec.vRefNum;
  1140.     d->dirID    =    spec.parID;
  1141.     
  1142.     return d;
  1143.     
  1144. error:
  1145.     File_error(spec.Error());
  1146.     
  1147.     return nil;
  1148. }
  1149.     
  1150. struct dirent * readdir(DIR * dirp)
  1151. {
  1152.     TFileSpec    spec;
  1153.     int             i; 
  1154.     int            e = errno;
  1155.     
  1156.     spec.vRefNum    =    dirp->vol;
  1157.     spec.parID        =    dirp->dirID;
  1158.     
  1159.     spec = spec[dirp->index++];
  1160.     
  1161.     if (spec.Error())
  1162.         goto error;
  1163.     
  1164.     dirp->entry.d_fileno    =    spec.LastInfo()->dirInfo.ioDrDirID;
  1165.     dirp->entry.d_namlen = *spec.name;
  1166.     
  1167.     memcpy(dirp->entry.d_name, (char *) spec.name+1, dirp->entry.d_namlen);
  1168.     
  1169.     dirp->entry.d_name[dirp->entry.d_namlen] = 0;
  1170.     
  1171.     i = dirp->entry.d_namlen;
  1172.     do 
  1173.         dirp->entry.d_name[i++] = 0;
  1174.     while (i & 3);
  1175.     
  1176.     dirp->entry.d_reclen = sizeof(u_long)+sizeof(u_short)+sizeof(u_short)+i;
  1177.     
  1178.     return &dirp->entry;
  1179.     
  1180. error:
  1181.     File_error(spec.Error());
  1182.     
  1183.     if (errno == ENOENT)
  1184.         errno = e;
  1185.         
  1186.     return nil;
  1187. }
  1188.  
  1189. long telldir(const DIR * dirp)
  1190. {
  1191.     return dirp->index;
  1192. }
  1193.  
  1194. void seekdir(DIR * dirp, long loc)
  1195. {
  1196.     dirp->index    =    (short) loc;
  1197. }
  1198.  
  1199. void rewinddir(DIR * dirp)
  1200. {
  1201.     dirp->index    =    1;
  1202. }
  1203.  
  1204. int closedir(DIR * dirp)
  1205. {
  1206.     DisposPtr((Ptr) dirp);
  1207.     
  1208.     return 0;
  1209. }
  1210.  
  1211. int scandir(
  1212.     const char *         name, 
  1213.     struct dirent *** namelist,
  1214.    int (*want)(struct dirent *), 
  1215.     int (*cmp)(const void *, const void *))
  1216. {
  1217.     struct dirent *    entry;
  1218.     struct dirent *    copy;
  1219.     struct dirent **    names;
  1220.     int                     count;
  1221.     DIR *                    dirp;
  1222.     CInfoPBRec            info;
  1223.     TFileSpec            spec;
  1224.     
  1225.     if ((dirp = opendir(name)) == NULL)
  1226.         return -1;
  1227.  
  1228.     spec.vRefNum    =    dirp->vol;
  1229.     spec.parID        =    dirp->dirID;
  1230.  
  1231.     if (spec.CatInfo(info))
  1232.         return File_error(spec.Error());
  1233.  
  1234.     names = (struct dirent **) malloc(info.dirInfo.ioDrNmFls * sizeof(struct dirent *));
  1235.     if (names == NULL)
  1236.         return GUSI_error(ENOMEM);
  1237.  
  1238.     count = 0;
  1239.     while ((entry = readdir(dirp)) != NULL) {
  1240.         if (want && !(*want)(entry))
  1241.             continue;    /* Don't want this entry */
  1242.  
  1243.         if (!(copy = (struct dirent *)malloc(entry->d_reclen))) {
  1244.             free(names);
  1245.             
  1246.             return GUSI_error(ENOMEM);
  1247.         }
  1248.         
  1249.         memcpy(copy, entry, entry->d_reclen);
  1250.  
  1251.         names[count++] = copy;
  1252.     }
  1253.     
  1254.     closedir(dirp);
  1255.     
  1256.     if (count && cmp)
  1257.         qsort(names, count, sizeof(struct dirent *), cmp);
  1258.         
  1259.     *namelist = names;
  1260.     
  1261.     return count;
  1262. }
  1263.  
  1264. int chdir(const char * path)    
  1265. {
  1266.     TFileSpec    dir(path);
  1267.     
  1268.     if (dir.Error())
  1269.         return GUSI_error(ENOENT);
  1270.         
  1271.     return File_error(TFileSpec::ChDir(dir));
  1272. }
  1273.  
  1274. int mkdir(const char * path)    
  1275. {
  1276.     OSErr            err;
  1277.     long            nuDir;
  1278.     TFileSpec    dir(path, true);
  1279.     
  1280.     if (dir.Error())
  1281.         return File_error(dir.Error());
  1282.     
  1283.     if (err = DirCreate(dir.vRefNum, dir.parID, dir.name, &nuDir))
  1284.         return File_error(err);
  1285.  
  1286.     return 0;
  1287. }
  1288.  
  1289. int rmdir(const char * path)    
  1290. {
  1291.     OSErr            err;
  1292.     TFileSpec    dir(path);
  1293.     
  1294.     if (dir.Error())
  1295.         return GUSI_error(ENOENT);
  1296.  
  1297.     if (err = HDelete(dir.vRefNum, dir.parID, dir.name))
  1298.         switch (err)    {
  1299.         default:
  1300.             return File_error(err);
  1301.         case dirFulErr:
  1302.             return GUSI_error(ENOTEMPTY);
  1303.         }
  1304.     
  1305.     return 0;
  1306. }
  1307.  
  1308. char * getcwd(char * buf, size_t size)
  1309. {
  1310.     OSErr            err;
  1311.     TFileSpec    cwd;
  1312.     char *         res;
  1313.     
  1314.     if (err = cwd.Default()) {
  1315.         File_error(err);
  1316.     
  1317.         return nil;
  1318.     }
  1319.         
  1320.     res = cwd.FullPath();
  1321.     
  1322.     if (size < strlen(res)+1)
  1323.         return (char *) GUSI_error_nil(ENAMETOOLONG);
  1324.     if (!buf && !(buf = (char *) malloc(size)))
  1325.         return (char *) GUSI_error_nil(ENOMEM);
  1326.  
  1327.     strcpy(buf, res);
  1328.     
  1329.     return buf;
  1330. }